package com.huawei.app.demo.video;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.ArrayBlockingQueue;

import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.view.Surface;

import com.huawei.app.demo.util.LogUtils;
import com.huawei.app.httpapi.ThreadPools;

/**
 * 编码任务
 *
 * @since 2021-02-27
 */
public class AvcEncoderTask implements Runnable {

    private static final String TAG = AvcEncoderTask.class.getSimpleName();

    public boolean isRuning = true;
    private MediaCodec mediaCodecEncoder; // 编码器
    private Surface inputSurface;         // 编码器的输入Surface
    private ArrayBlockingQueue<ImgBuffer> senderQueue;

    private int width;              // 宽度
    private int height;             // 高度
    private int framerate;          // 帧率
    private int bitrate = 12000000; // 码率
    private int TIMEOUT_USEC = 12000;

    public AvcEncoderTask(int width, int height, int framerate, ArrayBlockingQueue<ImgBuffer> senderQueue) {
        this.width = width;
        this.height = height;
        this.framerate = framerate;
        this.senderQueue = senderQueue;
    }

    /**
     * 初始化编码器
     * @return
     */
    public Surface initEncoder() {
        try {
            // 初始化编码器
            mediaCodecEncoder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);
            // mediaCodecEncoder = MediaCodec.createByCodecName("OMX.google.h264.encoder");
            // mediaCodecEncoder = MediaCodec.createByCodecName("OMX.hisi.video.encoder.avc");
            MediaFormat mediaFormatEncoder = createMediaFormat(MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
            mediaCodecEncoder.configure(mediaFormatEncoder, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
            inputSurface = mediaCodecEncoder.createInputSurface();
            mediaCodecEncoder.start();
            LogUtils.getInstance().i(TAG, "init encoder success.");
        } catch (IOException e) {
            LogUtils.getInstance().e(TAG, "init encoder failed.", e);
        }
        return inputSurface;
    }

    private MediaFormat createMediaFormat(int keyColorFormat) {
        MediaFormat mediaFormat = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, width, height);
        mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, keyColorFormat); // 颜色格式

        // 码率模式
        // CQ 不控制码率, 尽最大可能保证图像质量
        // CBR 静态码率
        // VBR 动态码率, 根据图像内容的复杂度来动态调整输出码率, 图像复杂则码率高, 图像简单则码率低
        mediaFormat.setInteger(MediaFormat.KEY_BITRATE_MODE, MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_VBR);
        mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitrate); // 码率
        mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, framerate); // 帧率
        mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1); // 帧间间隔, 单位秒
        mediaFormat.setInteger(MediaFormat.KEY_PROFILE, MediaCodecInfo.CodecProfileLevel.AVCProfileBaseline); // H264 BP
        mediaFormat.setInteger(MediaFormat.KEY_LEVEL, MediaCodecInfo.CodecProfileLevel.AVCLevel41);

        LogUtils.getInstance().i(TAG, "createMediaFormat "
                + " profile: " + mediaFormat.getInteger(MediaFormat.KEY_PROFILE) + ", "
                + " level: " + mediaFormat.getInteger(MediaFormat.KEY_LEVEL));
        return mediaFormat;
    }

    @Override
    public void run() {
        String oldThreadName = Thread.currentThread().getName();
        Thread.currentThread().setName(this.getClass().getSimpleName());

        long sno = 0;
        byte[] imgDataOut;
        MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
        while (isRuning) {
            try {
                long beginTime = System.currentTimeMillis();

                // 获取编码后的数据 -> 放入发送队列
                int sendCount = 0;
                int dropCount = 0;
                int outputBufferIndex = mediaCodecEncoder.dequeueOutputBuffer(bufferInfo, TIMEOUT_USEC);
                while (outputBufferIndex >= 0) {
                    imgDataOut = new byte[bufferInfo.size];

                    ByteBuffer outputBuffer = mediaCodecEncoder.getOutputBuffer(outputBufferIndex);
                    outputBuffer.get(imgDataOut);
                    sno++;

                    sendCount++;
                    ImgBuffer imgBuffer = new ImgBuffer(sno, width, height, sendCount, imgDataOut);
                    boolean offerSuccess = AvcEncoderTask.this.senderQueue.offer(imgBuffer);
                    if (!offerSuccess) {
                        dropCount++;
                    }

                    mediaCodecEncoder.releaseOutputBuffer(outputBufferIndex, false);
                    outputBufferIndex = mediaCodecEncoder.dequeueOutputBuffer(bufferInfo, TIMEOUT_USEC);
                }

                long endTime = System.currentTimeMillis();
                long time = endTime - beginTime;
                LogUtils.getInstance().v(TAG, "encode time: " + time
                        + ", senderQueue.size: " + senderQueue.size()
                        + ", sno: " + sno
                        + ", sendCount: " + sendCount
                        + ", dropCount: " + dropCount);

                // 每秒最大获取40帧图像 1000/25 = 40
                ThreadPools.pause(25);
            } catch (Exception e) {
                LogUtils.getInstance().e(TAG, e.getMessage());
            }
        }

        Thread.currentThread().setName(oldThreadName);
    }

    /**
     * Generates the presentation time for frame N, in microseconds.
     */
    private long computePresentationTime(long frameIndex) {
        // 1秒 = 1000毫秒(milliseconds) = 1000000微秒(microseconds)
        // 1000000 / framerate 表示帧间间隔
        return 132 + frameIndex * 1000000 / framerate;
    }

    public void stop(){
        if (!isRuning) {
            return;
        }

        try {
            isRuning = false;
            if (mediaCodecEncoder != null) {
                mediaCodecEncoder.stop();
                mediaCodecEncoder.release();
            }
        } catch (Exception e){
            LogUtils.getInstance().e(TAG, e.getMessage());
        } finally {
            mediaCodecEncoder = null;
        }
    }
}
